{This file is part of CheLive.

CheLive is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Foobar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar.  If not, see <http://www.gnu.org/licenses/>.}

{Модуль реализует список значений и другие базовые типы, а также вспомагательные функции}
unit CoreTypes;

interface

uses
	CoreMath;

const
	{Величина увеличения списка}
	LIST_ITER = 8;

type
	{Элемент списка @seealso(TList)
	@member(name имя)}
	TListItem = class
		name: String;
	end;

	{Список элементов
	@member(items массив элементов)
	@member(size размер списка (может увеличиваться при добавлении элемента, это макс. кол-во занятых ячеек в массиве items))
	@member(realSize реальный размер списка(размер массива items, увеличивается на LIST_ITER при добавлении элемента
	если нет больше свободных ячеек и size = realSize (т.е. когда в использвании весь массив items)))
	@member(factSize фактический размер списка (увеличивается/уменшается на 1 при добавлении/удалении элемента))
	@member(itemsByIndex получение элемента по числовому индексу)
	@member(itemsByName получение элемента по имени @seealso(TListItem.name))}
{$IFDEF PASDOC}
	TList = class
{$ELSE}
	generic TList<T> = class
{$ENDIF}
	private
		function GetItem(index: Integer): T;
		procedure SetItem(index: Integer; value: T);
		function GetItemByName(index: String): T;
	public
		items: array of T;
		size, realSize, factSize: Integer;

		property itemsByIndex[index: Integer]: T read GetItem write SetItem; default;
		property itemsByName[index: String]: T read GetItemByName;
		
		{Добавление элемента
		@param(item элемент)
		@returns(элемент (item))}
		function Add(item: T): T;
		{Поиск элемента по имени
		@param(name имя)
		@returns(элемент или nil (если нет элемента с таким именем))}
		function Find(name: String): T;
		{Получение индекса элемента
		@param(item элемент)
		@returns(индекс элемента в массиве items)}
		function IndexOf(item: T): Integer;
		{Удаление элемента
		@param(item элемент)}
		procedure Rem(item: T);

		constructor Create;
		destructor Destroy; override;
	end;

type
	PRect = ^TRect;
	TRect = record
		x, y, w, h: Single;
	end;

	TInt3 = array[0..2] of Integer;
	TProc = procedure();
	TStrings = array of String;
	TBytes = array of Byte;

{Формирует квадрат}
function Rect(x, y, w, h: Single): TRect; inline;
{Формирует квадрат по двум точкам}
function Rect(pos, size: TVec2): TRect; inline;
{Разбивает (взрывает! :)) строку
@param(delim строка-разделитель)
@param(str разделяемая строка)
@returns(массив строк)}
function Explode(delim, str: String): TStrings;
function UTF8ToUnicode(str: String): WideString;

implementation

uses
	SysUtils;

constructor TList.Create;
begin
	size := 0;
	realSize := 0;
	factSize := 0;
end;

destructor TList.Destroy;
var
	i: Integer;
begin
	for i := 0 to size - 1 do
	begin
		FreeAndNil(TListItem(items[i]));
	end;

	inherited;
end;

function TList.GetItem(index: Integer): T;
begin
	Result := items[index];
end;

procedure TList.SetItem(index: Integer; value: T);
begin
	items[index] := value;
end;

function TList.GetItemByName(index: String): T;
begin
	Result := Find(index);
end;

function TList.Add(item: T): T;
var
	i: Integer;
begin
	Result := item;

	factSize += 1;

	for i := 0 to size - 1 do
	begin
		if TListItem(items[i]) = nil then
		begin
			items[i] := item;
			Exit;
		end;
	end;

	if size = realSize then
	begin
		SetLength(items, realSize + LIST_ITER);
		FillChar(items[realSize], LIST_ITER * SizeOf(TListItem), 0);
		realSize += LIST_ITER;
	end;

	items[size] := item;
	size += 1;
end;

function TList.Find(name: String): T;
var
	i: Integer;
begin
	Result := nil;

	for i := 0 to size - 1 do
	begin
		if (TListItem(items[i]) <> nil) and (TListItem(items[i]).name = name) then
		begin
			Result := items[i];
			Exit;
		end;
	end;
end;

function TList.IndexOf(item: T): Integer;
var
	i: Integer;
begin
	Result := -1;

	for i := 0 to size - 1 do
	begin
		if items[i] = item then
		begin
			Result := i;
			Exit;
		end;
	end;
end;

procedure TList.Rem(item: T);
var
	i: Integer;
begin
	for i := 0 to size - 1 do
	begin
		if items[i] = item then
		begin
			items[i] := nil;
			factSize -= 1;
			Exit;
		end;
	end;
end;

function Rect(x, y, w, h: Single): TRect;
begin
	Result.x := x;
	Result.y := y;
	Result.w := w;
	Result.h := h;
end;

function Rect(pos, size: TVec2): TRect;
begin
	Result.x := pos.x;
	Result.y := pos.y;
	Result.w := size.x;
	Result.h := size.y;
end;

function Explode(delim, str: String): TStrings;
var
  str2: String;
  i: Integer;
begin
  i := 0;
  str2 := str + delim;

  repeat
    SetLength(Result, i + 1);
    Result[i] := Copy(str2, 0, Pos(delim, str2) - 1);
    Delete(str2, 1, Length(Result[i]) + Length(delim));
    Inc(i);
  until str2 = '';
end;

function UTF8ToUnicode(str: String): WideString;
var
	i, j, k: Integer;
begin
	i := 1;
	j := 0;
	
	while i <= Length(str) do
	begin
		if Ord(str[i]) < 128 then i += 1 else
		if Ord(str[i]) < 224 then i += 2 else
		if Ord(str[i]) < 240 then i += 3 else
		if Ord(str[i]) < 248 then i += 4;
		
		j += 1;
	end;
	
	SetLength(Result, j);
	
	i := 1;
	k := 1;
	
	while i <= Length(str) do
	begin
		if Ord(str[i]) < 128 then
		begin
			j := Ord(str[i]);
			i += 1;
		end else
		
		if Ord(str[i]) < 224 then
		begin
			j := (Ord(str[i]) - 192) * 64 + Ord(str[i + 1]) - 128;
			i += 2;
		end else
		
		if Ord(str[i]) < 240 then
		begin
			j := (Ord(str[i]) - 224) * 4096 + (Ord(str[i + 1]) - 128) * 64 + Ord(str[i + 2]) - 128;
			i += 3;
		end else
		
		if Ord(str[i]) < 248 then
		begin
			j := (Ord(str[i]) - 240) * 262144 + (Ord(str[i + 1]) - 128) * 4096 + (Ord(str[i + 2]) - 128) * 64 + Ord(str[i + 3]) - 128;
			i += 4;
		end;
		
		Result[k] := WideChar(j);
		
		k += 1;
	end;
end;

end.
